package com.micro.trace.log.filter;

import com.micro.trace.log.bean.LogBean;
import com.micro.trace.log.io.FastStringPrintWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.UriUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.stream.Collectors;

import static com.micro.trace.log.cants.Constant.KEY_EXCEPTION;

public class TraceLogFilter extends OncePerRequestFilter implements Ordered {

    private static final Logger logger = LoggerFactory.getLogger(OncePerRequestFilter.class);

    @Override
    public int getOrder() {
        return 0;
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestUrl = request.getRequestURL().toString();
        String queryString = request.getQueryString();
        if (!StringUtils.isEmpty(queryString)) {
            queryString = UriUtils.decode(queryString, request.getCharacterEncoding());
            requestUrl += "?" + queryString;
        }
        String method = request.getMethod();
        String params = request.getParameterMap().entrySet().stream()
                .map(entry -> entry.getKey() + "=" + String.join(",", Arrays.asList(entry.getValue())))
                .collect(Collectors.joining("&"));
        RequestCachingRequestWrapper cachingRequestWrapper = new RequestCachingRequestWrapper(request);
        ContentCachingResponseWrapper cachingResponseWrapper = new ContentCachingResponseWrapper(response);
        String throwableMessage;
        try {
            filterChain.doFilter(cachingRequestWrapper, cachingResponseWrapper);
        } catch (Throwable ex) {
            try (FastStringPrintWriter pw = new FastStringPrintWriter()) {
                ex.printStackTrace(pw);
                throwableMessage = pw.toString();
            }
        } finally {
            byte[] respBytes = cachingResponseWrapper.getContentAsByteArray();
            if (!response.isCommitted()) {
                response.getOutputStream().write(respBytes);
            }
            byte[] reqBytes = cachingRequestWrapper.getContentAsByteArray();
            String payload = new String(reqBytes, cachingRequestWrapper.getCharacterEncoding());
            LogBean log = new LogBean(cachingResponseWrapper.getStatus()
                    , "请求日志埋点"
                    , requestUrl
                    , method
                    , ""
                    , params
                    , payload
                    , ""
                    , new String(respBytes, cachingResponseWrapper.getCharacterEncoding())
                    , ""
                    , "");
            log(cachingRequestWrapper, cachingResponseWrapper, log.toString());
        }

    }

    private void log(HttpServletRequest request, HttpServletResponse response, String msg) {
        if (200 != response.getStatus()
                || request.getAttribute(KEY_EXCEPTION) != null) {
            logger.error(msg);
        } else {
            logger.info(msg);
        }
    }


}
